'use strict';
const Mustache = require('ejs');
const os = require('os');
const {RzTelnet,createCMD,generateCMD} = require('../RZtelnet/next');
const ApolloBoost = require('apollo-boost');
const fetch = require('node-fetch');
const {
  createHttpLink,
} = require('apollo-link-http');
const {
  ApolloClient,
  InMemoryCache,
} = ApolloBoost;
const site_server = 'http://localhost:4000/graphql';
// const site_server = 'http://www.optictimesolt.com/graphql';
async function getConnectParam(type,ip,me){
  try {
    let {ctx,proxyFlag} = me;
    //从cookie中取token
    let str = ctx.cookies.get('token', {
      httpOnly: true,
      signed: true, // 对cookie进行签名 防止用户修改cookie
      encrypt: true, // 是否对cookie进行加密 如果cookie加密那么获取的时候要对cookie进行解密,这里要注意配置文件里的cookie.key和授权中心的key保持一致
    })
    let tenant={}; //用于存放租户信息的对象
    if(!str){
      // websocket没有传递token过来
      str = me.ctx.tenant;
      tenant = JSON.parse(str);
    }else{
      //解析token
      const cookieObj = JSON.parse(str)
      tenant = cookieObj.user.tenant;
    }
    //从租户上获取穿透的用户名和tid
    let {cloudOltState,cloudOltUsername,id:tid} = tenant;
    proxyFlag = proxyFlag||cloudOltState;
    if(proxyFlag){ //需要走穿透
      //现在想调用新版穿透的代码
      //先查询olt的连接映射端口号
      let [_ip,_port] = ip.split(':')
      let result = await me.ctx.curl(`http://optictimes.online:9200/query?agentid=${cloudOltUsername}&addr=${_ip}&type=${type}`)
      let {status,data} = result;
      if(status===200){
        //更改ip连接地址。
        ip = data.toString();
      }else{
        console.log('查询穿透地址有问题',result.data.toString())
        return null;
      }
    }

    return {
      tid,
      ip,
      proxyFlag
    }
  } catch (error) {
    throw error;
  }
}
async function run_EX(ip, params, fn, me,destroySocketFlag) {
  return new Promise(async (resolve, reject) => {
    try {
      //step 1 判断是否走穿透，得到最终的连接信息
      let target = await getConnectParam('telnet',ip,me);
      if(!target){
        reject("getConnectParam return null")
      }
      let {tid, ip:destIP, proxyFlag} = target;
      // step 2 load script
      const script = await concatScripts(params.oltType, params.mountPoint, params.oltHardVersion,tid, me);
      // step 3 get command array
      const cmdArray = getCMDArray_EX(script, params.view, fn);
      // step 4 生成命令数组
      const CMD = generateCMD(
        createCMD('loginOLT', params.view.username, params.view.password),
        createCMD('management', params.oltType, params.view.ManagementPassword),
        cmdArray,
        createCMD('quitOLT',params.oltType),
        createCMD('quitCloudOLT')
      )
      //step 5 执行命令 返回命令结果
      const resultArr = await RzTelnet(CMD,proxyFlag,destIP,destroySocketFlag);
      resolve(resultArr);
    } catch (error) {
      reject(error);
    }
  });
}
async function loadScript(oltType, mountPoint, version, isConcat,tid, me) {
  return new Promise(async (resolve, reject) => {
    // 加载模板
    const oltTypeInfo = await getScript(mountPoint, version,tid, me);
    if (oltTypeInfo == null) {
      return reject(new Error(`There is no script for ${oltType}-${version}-${mountPoint}`));
    }
    if (oltTypeInfo.scripts.length !== 1) {
      return reject(new Error(`multiple scripts for ${oltType}-${version}-${mountPoint}`));
    }
    let script = oltTypeInfo.scripts[0].script;
    script = script.replace(/<br>/g, '\n');

    const result = await scriptWrap(script, isConcat,tid,me);
    resolve(result);
    async function getScript(mountPoint, version,tid, me) {
      return await me.ctx.modelmysql.OltType.findOne({
        where: {
          name: oltType,
          tid,
        },
        include: [{
          model: me.ctx.modelmysql.Script,
          where: {
            MountPoint: mountPoint,
            version,
            tid,
          },
        }],
      });
    }
    async function scriptWrap(script, flag,tid,me) {
      return new Promise(async (resolve, reject) => {
        try {
          if (flag) {
            // 组合脚本去掉include login
            script = script.replace(/<%\s*include\s*LOGIN\s*%>/ig, '');
          }
          // if (script.indexOf('include') != -1) {
          const array = script.match(/<%\s*include(.*)%>/ig);
          if (array) {
            // let array = script.match(/<%\s*include(.*)%>/ig) || []
            // 匹配include数组，遍历替换对应脚本
            for (let i = 0; i < array.length; i++) {
              const tempArray = array[i].match(/<%\s*include(.*)%>/i);
              const includeMountPoint = tempArray ? tempArray[1].trim() : '';
              const result = await getScript(includeMountPoint, version,tid,me);
              const includeScript = result.scripts[0].script;
              script = script.replace(new RegExp(tempArray[0], 'ig'), includeScript);
            }
            script = script.replace(/<br>/g, '\n');
          }
          resolve(script);
        } catch (error) {
          reject(error);
        }
      });
    }
  });
}

async function concatScripts(oltType, scriptMountPoint, version,tid, me) {
  let result = '';
  if (typeof scriptMountPoint !== 'string') {
    // 将多个脚本组合成一个脚本
    for (let i = 0; i < scriptMountPoint.length; i++) {
      if (i === 0) {
        result += await loadScript(oltType, scriptMountPoint[i], version, false,tid, me);
      } else {
        result += await loadScript(oltType, scriptMountPoint[i], version, true,tid, me);
      }
    }
  } else {
    // 只加载一个脚本
    result += await loadScript(oltType, scriptMountPoint, version, false,tid, me);
  }
  return result;
}

function getCMDArray_EX(script, view, fn) {
  const template = Mustache.compile(script);
  const output = template(view);
  // 组装命令数组
  const seq = output;
  const s = seq.trim();
  const rows = s.split('\n');
  const cmdArray = [];
  for (let i = 0; i < rows.length; i++) {
    const line = rows[i];
    if (line === '') {
      continue;
    }
    const obj = {};

    const arr = line.split('$');
    if (arr.length <= 1) {
      // console.log(`error on line ${i}:${rows[i]}`  )
    }
    arr[0] = arr[0].replace(/&gt;/g, '>').replace(/&lt;/g, '<');
    obj.expect = new RegExp(arr[0]);
    if (arr.length > 1) {
      if (arr[1].trim() === 'ENTER') {
        obj.send = ' ';
      } else if (arr[1]) {
        obj.send = arr[1].trim() ;
        // obj.send = arr[1].trim() + '\r';
      } else {
        obj.send = ' ';
      }

    }
    if (arr.length > 2 && arr[2].indexOf('RETURN') !== -1) { obj.out = true; }
    if (arr.length > 2 && arr[2].indexOf('BREAK') !== -1) {
      obj.break = true;
      obj.fn = fn;
    }
    if (arr.length > 3 && arr[3].indexOf('BREAK') !== -1) {
      obj.break = true;
      obj.fn = fn;
    }
    if (arr.length > 3 && arr[3].indexOf('RETURN') !== -1) { obj.out = true; }
    cmdArray.push(obj);
  }
  return cmdArray;
}

function getIP() {
  const ifaces = os.networkInterfaces();
  for (const dev in ifaces) {
    for (const index in ifaces[dev]) {
      if (ifaces[dev][index].family === 'IPv4') {
        const myip = ifaces[dev][index].address;
        if (myip !== '127.0.0.1' && !dev.match(/VMware/)) {
          return myip;
        }
      }
    }
  }
}
async function query(query, variables = {}) {
  return new Promise((resolve, reject) => {
    const client = new ApolloClient({
      link: createHttpLink({
        uri: site_server,
        fetch,
      }),
      cache: new InMemoryCache(),
    });
    client.query({
      query,
      variables,
    }).then(res => {
      resolve(res);
    }).catch(err => {
      reject(err);
    });
  });
}

async function mutate(mutation, variables = {}) {
  return new Promise((resolve, reject) => {
    const client = new ApolloClient({
      link: createHttpLink({
        uri: site_server,
        fetch,
      }),
      cache: new InMemoryCache(),
    });
    client.mutate({
      mutation,
      variables,
    }).then(res => {
      resolve(res);
    }).catch(err => {
      reject(err);
    });
  });
}

module.exports = {
  loadScript,
  getIP,
  concatScripts,
  run_EX,
  query,
  mutate,
  getConnectParam
};
